package com.yupi.yupao.job;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yupao.model.domain.User;
import com.yupi.yupao.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 缓存预热任务->定时任务,每天刷新所有用户的推荐列表
 *
 * @author 曹宏福
 * @version 1.0
 */
@Component
@Slf4j
@SuppressWarnings({"all"})
public class PreCacheJob {
    @Resource
    private UserService userService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 重点用户
    private final List<Long> mainUserList = Arrays.asList(1L);

    // todo 3、Redisson实现分布式锁
    @Resource
    private RedissonClient redissonClient;

    //每天凌晨 2 点 30 分 0 秒执行缓存
    @Scheduled(cron = "0 26 16 * * *")   //自己设置时间测试
    public void doCacheRecommendUser() {
        //通过RedissonClient 获取到一个分布式锁
        RLock lock = redissonClient.getLock("yupao:precachejob:docache:lock");
        //尝试获取锁
        try {
            //lock.tryLock(0, -1, TimeUnit.MILLISECONDS) 尝试获取锁，其中 0 表示等待获取锁的时间为 0 毫秒，
            // -1 表示锁的过期时间为永久（不过在实际使用中，建议设置合理的过期时间以避免死锁）。
            // 如果成功获取到锁，则执行后续的缓存预热操作。
            if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                /**
                 * 缓存预热操作：
                 * 遍历重点用户列表 mainUserList，对于每个用户 ID，使用 UserService 的 page 方法查询前 20 条用户数据。
                 * 构建 Redis 缓存键 redisKey，格式为 yupao:user:recommend:{userId}。
                 * 使用 RedisTemplate 的 ValueOperations 将查询到的用户数据 userPage
                 * 写入 Redis 缓存，缓存过期时间为 30000 毫秒（即 30 秒）
                 * todo 直接将分页查询的用户结果存储到Redis中,未对用户数据脱敏处理->完善 finished
                 * todo 优化缓存预热
                 */
                System.out.println("getLock: " + Thread.currentThread().getId());
                for (Long userId : mainUserList) {
                    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                    List<User> list = userService.list(queryWrapper);
                    List<User> result = list.stream().map(user -> {
                        //用户脱敏
                        return userService.getSafetyUser(user);
                    }).collect(Collectors.toList());
                    //获取RedisKey
                    String RedisKey = userService.redisFormat(userId);
                    ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
                    // 写缓存
                    try {
                        /**
                         * 解决缓存雪崩的思路：将缓存失效时间分散开,再原有的失效时间基础上增加一个随机值,缓存的过期时间的重复率就会降低,就很难引发集体失效事件从而发生雪崩
                         * 生成(1,2)分钟的缓存过期时间
                         */
                        int timeOutMinuteValue = 1 + RandomUtil.randomInt(0, 2);
                        valueOperations.set(RedisKey, result, timeOutMinuteValue, TimeUnit.MINUTES);
                    } catch (Exception e) {
                        log.error("redis set key error", e);
                    }
                }
            }
        } catch (InterruptedException e) {
            log.error("doCacheRecommendUser error", e);
        } finally {
            //执行完缓存预热 释放自己获取得分布式锁
            //查看RedissonClient 线程中是否存在指定的分布式锁 如果存在 则释放改客户端获取到的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }
}
